﻿using System;
using System.Collections.Generic;
using Gdi = System.Drawing;
using GdiImaging = System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using iNet.Win32;

namespace iNet.Media.Imaging
{
	/// <summary>
	/// 實作 IBitmapImage 介面的點陣圖類別。
	/// </summary>
	public class BitmapImage : MediaBase, IBitmapImage
	{
		#region 欄位
		volatile BitmapSource _BitmapSource;
		double _DpiX;
		double _DpiY;
		volatile bool _IsDisposed;
		readonly bool _OwnsPixelBuffer = true;
		volatile BitmapPalette _Palette;
		volatile IntPtr _PixelBuffer;
		volatile int _PixelBufferLockCounter;
		volatile int _PixelBufferSize;
		volatile int _PixelBufferStride;
		PixelFormat _PixelFormat;
		readonly int _PixelHeight;
		readonly int _PixelWidth;
		IMediaFormat _RawFormat;
		readonly object _ReadWriteSyncRoot = new object();
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="imageSource">表示點陣圖來源的 System.Windows.Media.Imaging.BitmapSource 物件。</param>
		/// <exception cref="ArgumentNullException">imageSource 為 Null (Visual Basic 為 Nothing)。</exception>
		public BitmapImage(BitmapSource imageSource)
			: this(null, imageSource, false, null, null)
		{ }
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="mediaStorage">原始影像儲存來源。</param>
		/// <param name="imageSource">表示點陣圖來源的 System.Windows.Media.Imaging.BitmapSource 物件。</param>
		/// <param name="isReadOnly">影像是否為唯讀。</param>
		/// <param name="metadata">影像的中繼資料。</param>
		/// <param name="rawFormat">原始影像格式。</param>
		/// <exception cref="ArgumentNullException">imageSource 為 Null (Visual Basic 為 Nothing)。</exception>
		public BitmapImage(MediaStorage mediaStorage, BitmapSource imageSource, bool isReadOnly, IMetadata metadata, IMediaFormat rawFormat)
			: base(mediaStorage, isReadOnly, metadata)
		{
			//確認參數
			if (imageSource == null)
				throw new ArgumentNullException("imageSource");

			//初始化欄位
			if (isReadOnly && !imageSource.IsFrozen)
			{
				if (imageSource.CanFreeze)
					imageSource = imageSource.GetAsFrozen() as BitmapSource;
				else
					imageSource = ImageUtility.CopyBitmap(imageSource).GetAsFrozen() as BitmapSource;
			}
			_BitmapSource = imageSource;
			_DpiX = imageSource.DpiX;
			_DpiY = imageSource.DpiY;
			_Palette = imageSource.Palette;
			_PixelFormat = imageSource.Format;
			_PixelWidth = imageSource.PixelWidth;
			_PixelHeight = imageSource.PixelHeight;
			_RawFormat = rawFormat;
		}
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="pixelWidth">像素寬度。</param>
		/// <param name="pixelHeight">像素高度。</param>
		public BitmapImage(int pixelWidth, int pixelHeight)
			: this(pixelWidth, pixelHeight, 96, 96)
		{ }
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="pixelWidth">像素寬度。</param>
		/// <param name="pixelHeight">像素高度。</param>
		/// <param name="dpiX">水平解析度。</param>
		/// <param name="dpiY">垂直解析度。</param>
		public BitmapImage(int pixelWidth, int pixelHeight, double dpiX, double dpiY)
			: base(null, false, null)
		{
			//確認參數
			if (pixelWidth <= 0)
				throw new ArgumentOutOfRangeException("pixelWidth");
			if (pixelHeight <= 0)
				throw new ArgumentOutOfRangeException("pixelHeight");
			if (dpiX <= 0)
				throw new ArgumentOutOfRangeException("dpiX");
			if (dpiY <= 0)
				throw new ArgumentOutOfRangeException("dpiY");

			//建立點陣圖
			_DpiX = dpiX;
			_DpiY = dpiY;
			_PixelFormat = PixelFormats.Bgra32;
			_PixelWidth = pixelWidth;
			_PixelHeight = pixelHeight;
			this.CreatePixelBuffer();
		}
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="pixelWidth">像素寬度。</param>
		/// <param name="pixelHeight">像素高度。</param>
		/// <param name="dpiX">水平解析度。</param>
		/// <param name="dpiY">垂直解析度。</param>
		/// <param name="pixelFormat">像素格式。</param>
		/// <param name="palette">調色盤。</param>
		/// <param name="buffer">像素資料。</param>
		/// <param name="stride">掃描線的位元組大小。</param>
		public BitmapImage(int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, BitmapPalette palette, Array buffer, int stride)
			: base(null, false, null)
		{
			//確認參數
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (pixelWidth <= 0)
				throw new ArgumentOutOfRangeException("pixelWidth");
			if (pixelHeight <= 0)
				throw new ArgumentOutOfRangeException("pixelHeight");
			if (dpiX <= 0)
				throw new ArgumentOutOfRangeException("dpiX");
			if (dpiY <= 0)
				throw new ArgumentOutOfRangeException("dpiY");
			if (stride <= 0)
				throw new ArgumentOutOfRangeException("stride");

			//確認緩衝區大小
			int bufferSize = Buffer.ByteLength(buffer);
			int minBufferSize = (stride * pixelHeight);
			if (bufferSize < minBufferSize)
				throw new ArgumentException();
			bufferSize = Math.Min(bufferSize, minBufferSize);

			//取得點陣圖資訊
			_DpiX = dpiX;
			_DpiY = dpiY;
			_Palette = palette;
			_PixelFormat = pixelFormat;
			_PixelWidth = pixelWidth;
			_PixelHeight = pixelHeight;

			//建立像素緩衝區
			_PixelBuffer = Marshal.AllocHGlobal(bufferSize);
			_PixelBufferSize = bufferSize;
			_PixelBufferStride = stride;
			GCHandle bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
			try
			{
				Kernel32.CopyMemory(_PixelBuffer, bufferHandle.AddrOfPinnedObject(), bufferSize);
			}
			finally
			{
				bufferHandle.Free();
			}
		}
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="pixelWidth">像素寬度。</param>
		/// <param name="pixelHeight">像素高度。</param>
		/// <param name="dpiX">水平解析度。</param>
		/// <param name="dpiY">垂直解析度。</param>
		/// <param name="pixelFormat">像素格式。</param>
		/// <param name="palette">調色盤。</param>
		/// <param name="buffer">像素資料。</param>
		/// <param name="bufferSize">像素資料的位元組大小。</param>
		/// <param name="stride">掃描線的位元組大小。</param>
		public BitmapImage(int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, BitmapPalette palette, IntPtr buffer, int bufferSize, int stride)
			: base(null, false, null)
		{
			//確認參數
			if (buffer == IntPtr.Zero)
				throw new ArgumentException();
			if (pixelWidth <= 0)
				throw new ArgumentOutOfRangeException("pixelWidth");
			if (pixelHeight <= 0)
				throw new ArgumentOutOfRangeException("pixelHeight");
			if (dpiX <= 0)
				throw new ArgumentOutOfRangeException("dpiX");
			if (dpiY <= 0)
				throw new ArgumentOutOfRangeException("dpiY");
			if (stride <= 0)
				throw new ArgumentOutOfRangeException("stride");

			//確認緩衝區大小
			if (bufferSize < (stride * pixelHeight))
				throw new ArgumentException();

			//取得點陣圖資訊
			_DpiX = dpiX;
			_DpiY = dpiY;
			_Palette = palette;
			_PixelFormat = pixelFormat;
			_PixelWidth = pixelWidth;
			_PixelHeight = pixelHeight;

			//建立像素緩衝區
			_PixelBuffer = buffer;
			_PixelBufferSize = bufferSize;
			_PixelBufferStride = stride;
			_OwnsPixelBuffer = false;
		}
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="bitmap">表示點陣圖來源的 System.Drawing.Bitmap 物件。</param>
		/// <exception cref="ArgumentNullException">bitmap 為 Null (Visual Basic 為 Nothing)。</exception>
		public BitmapImage(Gdi.Bitmap bitmap)
			: this(bitmap.ToBitmapSource())
		{
			if (_BitmapSource != null)
				_BitmapSource.Freeze();
		}
		/// <summary>
		/// 初始化新的 BitmapImage 執行個體。
		/// </summary>
		/// <param name="mediaStorage">原始影像儲存來源。</param>
		/// <param name="bitmap">表示點陣圖來源的 System.Drawing.Bitmap 物件。</param>
		/// <param name="isReadOnly">影像是否為唯讀。</param>
		/// <param name="metadata">影像的中繼資料。</param>
		/// <param name="rawFormat">原始影像格式。</param>
		/// <exception cref="ArgumentNullException">bitmap 為 Null (Visual Basic 為 Nothing)。</exception>
		public BitmapImage(MediaStorage mediaStorage, Gdi.Bitmap bitmap, bool isReadOnly, IMetadata metadata, IMediaFormat rawFormat)
			: this(mediaStorage, bitmap.ToBitmapSource(), isReadOnly, metadata, rawFormat)
		{
			if (_BitmapSource != null)
				_BitmapSource.Freeze();
		}
		#endregion
		#region Clone
		/// <summary>
		/// 建立一份可修改的點陣圖複本。
		/// </summary>
		/// <returns>點陣圖的複本。</returns>
		public override MediaBase Clone()
		{
			lock (_ReadWriteSyncRoot)
			{
				BitmapImage newBitmap = new BitmapImage(null, this.ToImageSource() as BitmapSource, false, (this.Metadata == null ? null : (this.Metadata.Clone() as IMetadata)), _RawFormat);
				newBitmap.CreatePixelBuffer();
				return newBitmap;
			}
		}
		#endregion
		#region CopyPixels
		/// <summary>
		/// 複製指定矩形範圍中的像素資料。
		/// </summary>
		/// <param name="rect">指定的矩形範圍，Int32Rect.Empty 表示整個點陣圖。</param>
		/// <param name="pixelFormat">指定的像素格式。</param>
		/// <param name="palette">指定像素格式所需的調色盤。</param>
		/// <param name="buffer">提供像素資料存放的緩衝區指標。</param>
		/// <param name="bufferSize">緩衝區的位元組大小。</param>
		/// <param name="stride">掃描線的位元組大小。</param>
		public void CopyPixels(Int32Rect rect, PixelFormat pixelFormat, BitmapPalette palette, IntPtr buffer, int bufferSize, int stride)
		{
			lock (_ReadWriteSyncRoot)
			{
				//確認狀態
				if (_IsDisposed)
					throw new ObjectDisposedException("BitmapImage");

				//確認參數
				if (stride <= 0)
					throw new ArgumentOutOfRangeException("stride");
				if (bufferSize < (stride * _PixelHeight))
					throw new ArgumentOutOfRangeException("bufferSize");
				if (rect.IsEmpty)
					rect = new Int32Rect(0, 0, _PixelWidth, _PixelHeight);

				//直接複製資料
				if (_PixelFormat == pixelFormat
					&& _Palette == palette
					&& rect.X == 0
					&& rect.Y == 0
					&& rect.Width == _PixelWidth
					&& rect.Height == _PixelHeight)
				{
					//取得像素資料
					this.CreatePixelBuffer();

					//直接複製資料
					if (_PixelBufferStride == stride)
						Kernel32.CopyMemory(buffer, _PixelBuffer, _PixelBufferSize);
					else
					{
						int copyStride = Math.Min(_PixelBufferStride, stride);
						IntPtr srcBuffer = _PixelBuffer;
						for (int y = _PixelHeight; y > 0; --y, srcBuffer += _PixelBufferStride, buffer += stride)
							Kernel32.CopyMemory(buffer, srcBuffer, copyStride);
					}
					return;
				}

				//取得來源影像
				BitmapSource source = this.ToBitmapSource() as BitmapSource;
				if (_PixelFormat != pixelFormat || _Palette != palette)
					source = new FormatConvertedBitmap(source, pixelFormat, palette, 0.5);
				
				//複製像素資料
				source.CopyPixels(rect, buffer, bufferSize, stride);
			}
		}
		#endregion
		#region CreatePixelBuffer
		void CreatePixelBuffer()
		{
			//確認狀態
			if (_PixelBuffer != IntPtr.Zero)
				return;

			//計算緩衝區資訊
			_PixelBufferStride = (int)(_PixelWidth * Math.Ceiling(_PixelFormat.BitsPerPixel / 8.0) + 0.1);
			_PixelBufferSize = (_PixelBufferStride * _PixelHeight);

			//建立緩衝區
			_PixelBuffer = Marshal.AllocHGlobal(_PixelBufferSize);

			//複製像素資料
			if (_BitmapSource != null)
			{
				_BitmapSource.CopyPixels(new Int32Rect(0, 0, _PixelWidth, _PixelHeight), _PixelBuffer, _PixelBufferSize, _PixelBufferStride);
				_BitmapSource = null;
			}
		}
		#endregion
		#region Dispose
		/// <summary>
		/// 釋放媒體所使用的資源。
		/// </summary>
		/// <param name="disposing">True 表示需釋放 Managed 及 Unmanaged 資源，False 表示僅需釋放 Unmanaged 資源。</param>
		protected override void Dispose(bool disposing)
		{
			//釋放點陣圖
			lock (_ReadWriteSyncRoot)
			{
				_IsDisposed = true;
				_BitmapSource = null;
				this.FreePixelBuffer();
			}

			//關閉媒體來源
			//if (this.MediaStorage != null)
				//this.MediaStorage.Unlock();

			//呼叫基底
			base.Dispose(disposing);
		}
		#endregion
		#region DpiX
		/// <summary>
		/// 取得水平解析度 (DPI)。
		/// </summary>
		public double DpiX
		{
			get
			{
				return _DpiX;
			}
		}
		#endregion
		#region DpiY
		/// <summary>
		/// 取得垂直解析度 (DPI)。
		/// </summary>
		public double DpiY
		{
			get
			{
				return _DpiY;
			}
		}
		#endregion
		#region FreePixelBuffer
		void FreePixelBuffer()
		{
			if (_PixelBuffer != IntPtr.Zero && _PixelBufferLockCounter <= 0)
			{
				if (_OwnsPixelBuffer)
					Marshal.FreeHGlobal(_PixelBuffer);
				_PixelBuffer = IntPtr.Zero;
			}
		}
		#endregion
		#region GetAsReadOnly
		/// <summary>
		/// 取得唯讀版本的點陣圖。
		/// </summary>
		/// <returns>唯讀的點陣圖。</returns>
		public override IMedia GetAsReadOnly()
		{
			lock (_ReadWriteSyncRoot)
			{
				//確認狀態
				if (_IsDisposed)
					throw new ObjectDisposedException("BitmapImage");

				//建立唯讀點陣圖
				if (this.IsReadOnly)
					return this;
				BitmapImage newBitmap = new BitmapImage(null, this.ToImageSource() as BitmapSource, true, (this.Metadata == null ? null : (this.Metadata.Clone() as IMetadata)), _RawFormat);
				newBitmap.CreatePixelBuffer();
				return newBitmap;
			}
		}
		#endregion
		#region HasDirectPixelBuffer
		/// <summary>
		/// 取得值以確認是否擁有可直接存取的像素資料緩衝區。
		/// </summary>
		public bool HasDirectPixelBuffer
		{
			get
			{
				return true;
			}
		}
		#endregion
		#region LockPixelBuffer
		public void LockPixelBuffer()
		{
			lock (_ReadWriteSyncRoot)
			{
				if (_IsDisposed)
					throw new ObjectDisposedException("BitmapImage");
				++_PixelBufferLockCounter;
			}
		}
		#endregion
		#region Palette
		/// <summary>
		/// 取得調色盤。
		/// </summary>
		public BitmapPalette Palette
		{
			get
			{
				return _Palette;
			}
		}
		#endregion
		#region PixelBuffer
		/// <summary>
		/// 取得像素資料緩衝區的起始記憶體位址。
		/// </summary>
		public IntPtr PixelBuffer
		{
			get
			{
				lock (_ReadWriteSyncRoot)
				{
					if (_IsDisposed)
						throw new ObjectDisposedException("BitmapImage");
					this.CreatePixelBuffer();
					return _PixelBuffer;
				}
			}
		}
		#endregion
		#region PixelBufferStride
		/// <summary>
		/// 取得像素資料緩衝區的掃描線寬度，或稱為步距，單位為位元組。。
		/// </summary>
		public int PixelBufferStride
		{
			get
			{
				lock (_ReadWriteSyncRoot)
				{
					if (_IsDisposed)
						throw new ObjectDisposedException("BitmapImage");
					this.CreatePixelBuffer();
					return _PixelBufferStride;
				}
			}
		}
		#endregion
		#region PixelFormat
		/// <summary>
		/// 取得點陣圖的像素格式。
		/// </summary>
		public PixelFormat PixelFormat
		{
			get
			{
				return _PixelFormat;
			}
		}
		#endregion
		#region PixelHeight
		/// <summary>
		/// 取得影像的像素高度。
		/// </summary>
		public int PixelHeight
		{
			get
			{
				return _PixelHeight;
			}
		}
		#endregion
		#region PixelWidth
		/// <summary>
		/// 取得影像的像素寬度。
		/// </summary>
		public int PixelWidth
		{
			get
			{
				return _PixelWidth;
			}
		}
		#endregion
		#region RawFormat
		/// <summary>
		/// 取得原始影像格式。
		/// </summary>
		public IMediaFormat RawFormat
		{
			get
			{
				return _RawFormat;
			}
		}
		#endregion
		#region ToImageSource
		/// <summary>
		/// 取得表示此影像的 System.Windows.Media.ImageSource 物件。
		/// </summary>
		/// <returns>System.Windows.Media.ImageSource 物件。</returns>
		public ImageSource ToImageSource()
		{
			lock (_ReadWriteSyncRoot)
			{
				if (_IsDisposed)
					throw new ObjectDisposedException("BitmapImage");
				if (_BitmapSource == null)
				{
					if (_PixelBuffer != IntPtr.Zero)
					{
						_BitmapSource = BitmapSource.Create(_PixelWidth, _PixelHeight, _DpiX, _DpiY, _PixelFormat, _Palette, _PixelBuffer, _PixelBufferSize, _PixelBufferStride);
						_BitmapSource.Freeze();
						return _BitmapSource;
					}
					throw new InvalidOperationException();
				}
				if (!_BitmapSource.IsFrozen)
					_BitmapSource.Freeze();
				return _BitmapSource;
			}
		}
		#endregion
		#region UnlockPixelBuffer
		public void UnlockPixelBuffer()
		{
			lock (_ReadWriteSyncRoot)
			{
				if (_PixelBufferLockCounter <= 0)
					return;
				--_PixelBufferLockCounter;
				if (_PixelBufferLockCounter == 0 && _IsDisposed)
					this.FreePixelBuffer();
			}
		}
		#endregion
		#region WritePixels
		/// <summary>
		/// 將像素資料寫入指定的矩形範圍中。
		/// </summary>
		/// <param name="rect">指定的矩形範圍，Int32Rect.Empty 表示整個點陣圖。</param>
		/// <param name="pixelFormat">寫入的像素格式。</param>
		/// <param name="palette">寫入像素格式所需的調色盤。</param>
		/// <param name="buffer">像素資料的緩衝區指標。</param>
		/// <param name="bufferSize">緩衝區的位元組大小。</param>
		/// <param name="stride">掃描線的位元組大小。</param>
		/// <exception cref="InvalidOperationException">點陣圖為唯讀。</exception>
		public void WritePixels(Int32Rect rect, PixelFormat pixelFormat, BitmapPalette palette, IntPtr buffer, int bufferSize, int stride)
		{
			lock (_ReadWriteSyncRoot)
			{
				//確認狀態
				if (_IsDisposed)
					throw new ObjectDisposedException("BitmapImage");
				if (this.IsReadOnly)
					throw new InvalidOperationException();

				//確認參數
				if (stride <= 0)
					throw new ArgumentOutOfRangeException("stride");
				if (rect.IsEmpty)
					rect = new Int32Rect(0, 0, _PixelWidth, _PixelHeight);
				if (bufferSize < (rect.Height * stride))
					throw new ArgumentOutOfRangeException("bufferSize");

				//直接寫入緩衝區
				if (_PixelFormat == pixelFormat
					&& _Palette == palette
					&& rect.X == 0
					&& rect.Y == 0
					&& rect.Width == _PixelWidth
					&& rect.Height == _PixelHeight)
				{
					//取得像素資料
					this.CreatePixelBuffer();

					//直接複製資料
					if (_PixelBufferStride == stride)
						Kernel32.CopyMemory(_PixelBuffer, buffer, _PixelBufferSize);
					else
					{
						int copyStride = Math.Min(_PixelBufferStride, stride);
						IntPtr srcBuffer = buffer;
						IntPtr destBuffer = _PixelBuffer;
						for (int y = _PixelHeight; y > 0; --y, srcBuffer += stride, destBuffer += _PixelBufferStride)
							Kernel32.CopyMemory(destBuffer, srcBuffer, copyStride);
					}
					return;
				}

				//寫入像素資料
				bool isTempBuffer = false;
				try
				{
					//轉換像素格式
					if (_BitmapSource.Format != pixelFormat || _BitmapSource.Palette != palette)
					{
						WriteableBitmap tempBitmap = new WriteableBitmap(_PixelWidth, _PixelHeight, _DpiX, _DpiY, pixelFormat, palette);
						tempBitmap.WritePixels(rect, buffer, bufferSize, stride);
						stride = (int)(rect.Width * Math.Ceiling(_BitmapSource.Format.BitsPerPixel / 8.0) + 0.1);
						bufferSize = (rect.Height * stride);
						buffer = Marshal.AllocHGlobal(bufferSize);
						isTempBuffer = true;
						FormatConvertedBitmap convertedBitmap = new FormatConvertedBitmap(tempBitmap, _PixelFormat, _Palette, 0.5);
						convertedBitmap.CopyPixels(rect, buffer, bufferSize, stride);
					}

					//建立寫入的點陣圖
					WriteableBitmap writableBitmap;
					if (_BitmapSource != null)
					{
						writableBitmap = (_BitmapSource as WriteableBitmap);
						if (writableBitmap == null || !writableBitmap.IsFrozen)
							writableBitmap = new WriteableBitmap(_BitmapSource);
					}
					else if (_PixelBuffer != IntPtr.Zero)
					{
						writableBitmap = new WriteableBitmap(_PixelWidth, _PixelHeight, _DpiX, _DpiY, _PixelFormat, _Palette);
						writableBitmap.WritePixels(new Int32Rect(0, 0, _PixelWidth, _PixelHeight), _PixelBuffer, _PixelBufferSize, _PixelBufferStride);
					}
					else
						throw new InvalidOperationException();

					//寫入像素資料
					writableBitmap.WritePixels(rect, buffer, bufferSize, stride);
					if (_PixelBuffer != IntPtr.Zero)
					{
						writableBitmap.CopyPixels(new Int32Rect(0, 0, _PixelWidth, _PixelHeight), _PixelBuffer, _PixelBufferSize, _PixelBufferStride);
						_BitmapSource = null;
					}
					else
						_BitmapSource = writableBitmap;
				}
				finally
				{
					if (isTempBuffer)
						Marshal.FreeHGlobal(buffer);
				}
			}
		}
		#endregion
	}
}
